package org.javaboy.demo;

import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 通知/增强
 * <p>
 * 就是将目标方法拦截下来之后，要添加的代码
 * <p>
 * 有五种通知和增强
 * <p>
 * 1. 前置通知：目标方法执行之前执行
 * 2. 后置通知：目标方法执行之后执行
 * 3. 返回通知：拿到目标方法的返回值之后执行
 * 4. 异常通知：目标方法执行抛出异常的时候执行
 * 5. 环绕通知：集大成者
 */
public class LogAdvice {

    /**
     * 这个是环绕通知，环绕目标方法
     *
     * @param pjp
     * @return
     */
    public Object around(ProceedingJoinPoint pjp) {
        Object proceed;
        try {
            //这一句就相当于是前置通知了
            long startTime = System.currentTimeMillis();
            //这句话其实就是执行目标方法
            proceed = pjp.proceed();
            //这里就是后置通知了
            long endTime = System.currentTimeMillis();
            System.out.println(pjp.getSignature().getName() + " 方法执行耗时 " + (endTime - startTime) + " 毫秒");
        } catch (Throwable e) {
            //这里就是异常通知
            //xxx
            throw new RuntimeException(e);
        }
        return proceed;
    }

    /**
     * 前置通知
     */
    public void before(JoinPoint jp) {
        //获取目标方法的名字
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法开始执行了");
    }

    /**
     * 后置通知
     *
     * @param jp
     */
    public void after(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法执行结束了");
    }

    /**
     * 返回通知执行的时候，实际上已经知道目标方法的返回值是什么了
     * 这个方法的第二个参数，就是目标方法的返回值。
     * 注意第二个方法的参数，这个参数只有在和目标方法的返回值类型相匹配的时候，当前方法才会被触发
     *
     * @param jp
     */
    public void afterReturning(JoinPoint jp, Object val) {
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法执行返回 " + val);
    }

    /**
     * 这个是目标方法抛出异常的时候被触发。
     * 当抛出异常的时候，我们希望能够在这个方法中得知发生了什么
     * 注意异常类型
     *
     * @param jp
     */
    public void afterThrowing(JoinPoint jp, Throwable t) {
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法执行抛出 " + t.getMessage());
    }

}
